import org.json.simple.JSONArray
import org.json.simple.JSONObject
import org.json.simple.parser.JSONParser
import java.io._
import java.net.URL
import java.util.Date
import java.util.Random

import scala.util.{Failure, Try}

object Fetcher {
  type Song = (String, String)
  val ITER: Int = 40
  val NUMBER_OF_AUDIOS = 15
  val FLUSH_INTERVAL = 40

  def main(args: Array[String]) {
    var token = ""
    var name = ""
    val br = new BufferedReader(new InputStreamReader(System.in))
    println("Enter token: ")
    token = br.readLine
    println("Enter filename (press enter to make current date a name): ")
    name = br.readLine
    new Fetcher(token, new File(if ("" == name) new Date().toString else name), ITER).start()
  }

}

class Fetcher(token: String, file: File, iterations: Int) {
  import Fetcher._
  println("File: " + file.getName)
  val storage = new PrintWriter(file)

  def start() {
    val rand = new Random
    def getRandomUser = {
      val time = System.nanoTime
      val res = new Entry(rand.nextInt(20000000) + 1)
        .getUserMainInfo.flatMap(_.getAudios)
      val time_to_wait = 666 - (System.nanoTime - time) / 1000
      if (time_to_wait > 0) Thread.sleep(time_to_wait)
      res
    }
    val iter = Stream.continually(getRandomUser)
      .flatten.iterator.take(iterations)
    var i = 1
    for (e <- iter) {
      print(e.toString)
      e.writeTo(storage)
      println(i + "...")
      i += 1
      if (i % FLUSH_INTERVAL == 0) storage.flush()
    }
    storage.close()
  }

  class Entry(user_id: Int) {
    var user_name: String = null
    val music = scala.collection.mutable.Set.empty[Song]
    val videos = scala.collection.mutable.Set.empty[String]

    def getUserMainInfo: Option[Entry] = {
      val parser: JSONParser = new JSONParser
      Try(new URL("https://api.vk.com/method/users.get?" +
        "user_ids=" + user_id + "&fields=movies,music&name_case=nom&" +
        "v=5.27&access_token=" + token).openStream())
        .map(url => new InputStreamReader(url))
        .map(parser.parse)
        .map(obj => obj.asInstanceOf[JSONObject]
          .get("response").asInstanceOf[JSONArray].get(0).asInstanceOf[JSONObject])
        .map { obj => user_name = obj.get("first_name") + " " + obj.get("last_name"); obj}
        .map { obj =>
          val mov = obj.get("movies").asInstanceOf[String]
          Option(if (mov == "") null else mov).map { mov =>
          // TODO: parse movies string
          videos.add(mov); obj }.get
        }.toOption.map { _ => this }
    }

    def getAudios: Option[Entry] = {
      val parser: JSONParser = new JSONParser
      Try(new URL("https://api.vk.com/method/audio.get?" +
        "owner_id=" + user_id + "&need_user=0&count=" + NUMBER_OF_AUDIOS +
        "&v=5.27&access_token=" + token).openStream())
        .map(url => new InputStreamReader(url))
        .map(parser.parse)
        .map(obj => obj.asInstanceOf[JSONObject]
          .get("response").asInstanceOf[JSONObject]
          .get("items").asInstanceOf[JSONArray]
          .iterator())
        .map { it =>
            def iter: Stream[Song] =
              if (!it.hasNext)
                Stream.empty
              else {
                val obj = it.next().asInstanceOf[JSONObject]
                (obj.get("artist").asInstanceOf[String], obj.get("title").asInstanceOf[String]) #:: iter
              }
            val list = iter.take(NUMBER_OF_AUDIOS)
            list.foreach(music.add)
            if (list.isEmpty) null else list
          }.toOption.map { _ => this }
    }

    def writeTo(fw: PrintWriter) {
      fw.print(user_id + ";" + user_name.replace(';', ' ').replace('#', ' ') + ";")
      for (mus <- music) {
        fw.print(mus._1.replace(';', ' ').replace('#', ' ') + "#" +
          mus._2.replace(';', ' ').replace('#', ' ') + ";")
      }
      fw.print("###")
      for (vid <- videos) {
        fw.print(vid.replace(';', ',') + ";")
      }
      fw.println
    }
  }
}

